<!DOCTYPE html>
<html>
<head>
<title>Java8-Stream</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
/* Author: Nicolas Hery - http://nicolashery.com */
/* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */
/* Source: https://github.com/nicolahery/markdownpad-github */

/* RESET
=============================================================================*/

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
}

/* BODY
=============================================================================*/

body {
  font-family: Helvetica, arial, freesans, clean, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  color: #333;
  background-color: #fff;
  padding: 20px;
  max-width: 960px;
  margin: 0 auto;
}

body>*:first-child {
  margin-top: 0 !important;
}

body>*:last-child {
  margin-bottom: 0 !important;
}

/* BLOCKS
=============================================================================*/

p, blockquote, ul, ol, dl, table, pre {
  margin: 15px 0;
}

/* HEADERS
=============================================================================*/

h1, h2, h3, h4, h5, h6 {
  margin: 20px 0 10px;
  padding: 0;
  font-weight: bold;
  -webkit-font-smoothing: antialiased;
}

h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
  font-size: inherit;
}

h1 {
  font-size: 28px;
  color: #000;
}

h2 {
  font-size: 24px;
  border-bottom: 1px solid #ccc;
  color: #000;
}

h3 {
  font-size: 18px;
}

h4 {
  font-size: 16px;
}

h5 {
  font-size: 14px;
}

h6 {
  color: #777;
  font-size: 14px;
}

body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
  margin-top: 0;
  padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
  margin-top: 0;
  padding-top: 0;
}

h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
  margin-top: 10px;
}

/* LINKS
=============================================================================*/

a {
  color: #4183C4;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* LISTS
=============================================================================*/

ul, ol {
  padding-left: 30px;
}

ul li > :first-child, 
ol li > :first-child, 
ul li ul:first-of-type, 
ol li ol:first-of-type, 
ul li ol:first-of-type, 
ol li ul:first-of-type {
  margin-top: 0px;
}

ul ul, ul ol, ol ol, ol ul {
  margin-bottom: 0;
}

dl {
  padding: 0;
}

dl dt {
  font-size: 14px;
  font-weight: bold;
  font-style: italic;
  padding: 0;
  margin: 15px 0 5px;
}

dl dt:first-child {
  padding: 0;
}

dl dt>:first-child {
  margin-top: 0px;
}

dl dt>:last-child {
  margin-bottom: 0px;
}

dl dd {
  margin: 0 0 15px;
  padding: 0 15px;
}

dl dd>:first-child {
  margin-top: 0px;
}

dl dd>:last-child {
  margin-bottom: 0px;
}

/* CODE
=============================================================================*/

pre, code, tt {
  font-size: 12px;
  font-family: Consolas, "Liberation Mono", Courier, monospace;
}

code, tt {
  margin: 0 0px;
  padding: 0px 0px;
  white-space: nowrap;
  border: 1px solid #eaeaea;
  background-color: #f8f8f8;
  border-radius: 3px;
}

pre>code {
  margin: 0;
  padding: 0;
  white-space: pre;
  border: none;
  background: transparent;
}

pre {
  background-color: #f8f8f8;
  border: 1px solid #ccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px;
}

pre code, pre tt {
  background-color: transparent;
  border: none;
}

kbd {
    -moz-border-bottom-colors: none;
    -moz-border-left-colors: none;
    -moz-border-right-colors: none;
    -moz-border-top-colors: none;
    background-color: #DDDDDD;
    background-image: linear-gradient(#F1F1F1, #DDDDDD);
    background-repeat: repeat-x;
    border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
    border-image: none;
    border-radius: 2px 2px 2px 2px;
    border-style: solid;
    border-width: 1px;
    font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
    line-height: 10px;
    padding: 1px 4px;
}

/* QUOTES
=============================================================================*/

blockquote {
  border-left: 4px solid #DDD;
  padding: 0 15px;
  color: #777;
}

blockquote>:first-child {
  margin-top: 0px;
}

blockquote>:last-child {
  margin-bottom: 0px;
}

/* HORIZONTAL RULES
=============================================================================*/

hr {
  clear: both;
  margin: 15px 0;
  height: 0px;
  overflow: hidden;
  border: none;
  background: transparent;
  border-bottom: 4px solid #ddd;
  padding: 0;
}

/* TABLES
=============================================================================*/

table th {
  font-weight: bold;
}

table th, table td {
  border: 1px solid #ccc;
  padding: 6px 13px;
}

table tr {
  border-top: 1px solid #ccc;
  background-color: #fff;
}

table tr:nth-child(2n) {
  background-color: #f8f8f8;
}

/* IMAGES
=============================================================================*/

img {
  max-width: 100%
}
</style>
<script>
    document.addEventListener("DOMContentLoaded", function() {
        // 生成目录列表
        var outline = document.createElement("ul");
        outline.setAttribute("id", "outline-list");
        outline.style.cssText = "border: 1px solid #ccc;";
        document.body.insertBefore(outline, document.body.childNodes[0]);
        // 获取所有标题
        var headers = document.querySelectorAll('h1,h2,h3,h4,h5,h6');
        for (var i = 0; i < headers.length; i++) {
            var header = headers[i];
            var hash = _hashCode(header.textContent);
            // MarkdownPad2无法为中文header正确生成id，这里生成一个
            header.setAttribute("id", header.tagName + hash);
            // 找出它是H几，为后面前置空格准备
            var prefix = parseInt(header.tagName.replace('H', ''), 10);
            outline.appendChild(document.createElement("li"));
            var a = document.createElement("a");
            // 为目录项设置链接
            a.setAttribute("href", "#" + header.tagName + hash)
            // 目录项文本前面放置对应的空格
            a.innerHTML = new Array(prefix * 4).join('&nbsp;') + header.textContent;
            outline.lastChild.appendChild(a);
        }
    });
    // 类似Java的hash生成方式，为一段文字生成一段基本不会重复的数字
    function _hashCode(txt) {
         var hash = 0;
         if (txt.length == 0) return hash;
         for (i = 0; i < txt.length; i++) {
              char = txt.charCodeAt(i);
              hash = ((hash<<5)-hash)+char;
              hash = hash & hash; // Convert to 32bit integer
         }
         return hash;
    }
</script>
</head>
<body>
<h2>Stream初体验</h2>
<blockquote>
<p>A sequence of elements supporting sequential and parallel aggregate operations.</p>
</blockquote>
<p>我们来解读一下上面的那句话：</p>
<blockquote>
<ul>
<li>Stream是元素的集合，这点让Stream看起来用些类似Iterator；</li>
<li>可以支持顺序和并行的对原Stream进行汇聚的操作；</li>
</ul>
</blockquote>
<p>大家可以把Stream当成一个高级版本的Iterator。原始版本的Iterator，用户只能一个一个的遍历元素并对其执行某些操作；高级版本的Stream，用户只要给出需要对其包含的元素执行什么操作，比如“过滤掉长度大于10的字符串”、“获取每个字符串的首字母”等，具体这些操作如何应用到每个元素上，就给Stream就好了！</p>
<blockquote>
<pre><code> //Lists是Guava中的一个工具类
List&lt;Integer&gt; nums = Lists.newArrayList(1,null,3,4,null,6);
nums.stream().filter(num -&gt; num != null).count();
</code></pre>

</blockquote>
<p>上面这段代码是获取一个List中，元素不为null的个数。这段代码虽然很简短，但是却是一个很好的入门级别的例子来体现如何使用Stream，正所谓“麻雀虽小五脏俱全”。我们现在开始深入解刨这个例子，完成以后你可能可以基本掌握Stream的用法！</p>
<h3>剖析Stream通用语法</h3>
<p><img src="http://img04.taobaocdn.com/imgextra/i4/90219132/T2ycFgXQ8XXXXXXXXX_!!90219132.jpg" /></p>
<p>红色框中的语句是一个Stream的生命开始的地方，负责创建一个Stream实例；绿色框中的语句是赋予Stream灵魂的地方，把一个Stream转换成另外一个Stream，红框的语句生成的是一个包含所有nums变量的Stream，进过绿框的filter方法以后，重新生成了一个过滤掉原nums列表所有null以后的Stream；蓝色框中的语句是丰收的地方，把Stream的里面包含的内容按照某种算法来汇聚成一个值，例子中是获取Stream中包含的元素个数。</p>
<p><strong>总结一下使用Stream的基本步骤：</strong></p>
<blockquote>
<ul>
<li>创建Stream；</li>
<li>转换Stream，每次转换原有Stream对象不改变，返回一个新的Stream对象（<strong>可以有多次转换</strong>）；</li>
<li>对Stream进行聚合（Reduce）操作，获取想要的结果；</li>
</ul>
</blockquote>
<h2>创建Stream</h2>
<p>最常用的创建Stream有两种途径：</p>
<blockquote>
<ul>
<li>通过Stream接口的静态工厂方法（注意：Java8里接口可以带静态方法）；</li>
<li>通过Collection接口的默认方法（默认方法：Default method，也是Java8中的一个新特性，就是接口中的一个带有实现的方法，后续文章会有介绍）–stream()，把一个Collection对象转换成Stream</li>
</ul>
</blockquote>
<h3>使用Stream静态方法来创建Stream</h3>
<h4>1. of方法：</h4>
<blockquote>
<p>有两个overload方法，一个接受变长参数，一个接口单一值</p>
</blockquote>
<pre><code>Stream&lt;Integer&gt; integerStream = Stream.of(1, 2, 3, 5);
Stream&lt;String&gt; stringStream = Stream.of(&quot;taobao&quot;);
</code></pre>

<h4>2. generator方法：</h4>
<blockquote>
<p>生成一个无限长度的Stream，其元素的生成是通过给定的Supplier（这个接口可以看成一个对象的工厂，每次调用返回一个给定类型的对象）</p>
</blockquote>
<pre><code>Stream.generate(new Supplier&lt;Double&gt;() {
    @Override
    public Double get() {
        return Math.random();
    }
});
Stream.generate(() -&gt; Math.random());
Stream.generate(Math::random);
</code></pre>

<blockquote>
<p>三条语句的作用都是一样的，只是使用了lambda表达式和方法引用的语法来简化代码。每条语句其实都是生成一个无限长度的Stream，其中值是随机的。这个无限长度Stream是懒加载，一般这种无限长度的Stream都会配合Stream的limit()方法来用。</p>
</blockquote>
<h4>3. iterate方法：</h4>
<blockquote>
<p>也是生成无限长度的Stream，和generator不同的是，其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的。其中包含的元素可以认为是：seed，f(seed),f(f(seed))无限循环</p>
</blockquote>
<pre><code>Stream.iterate(1, item -&gt; item + 1).limit(10).forEach(System.out::println);
</code></pre>

<blockquote>
<p>这段代码就是先获取一个无限长度的正整数集合的Stream，然后取出前10个打印。千万记住使用limit方法，不然会无限打印下去。</p>
</blockquote>
<h3>通过Collection子类获取Stream</h3>
<p>这个在本文的第一个例子中就展示了从List对象获取其对应的Stream对象，如果查看Java doc就可以发现Collection接口有一个stream方法，所以其所有子类都都可以获取对应的Stream对象。</p>
<pre><code>public interface Collection&lt;E&gt; extends Iterable&lt;E&gt; {
    //其他方法省略
    default Stream&lt;E&gt; stream() {
        return StreamSupport.stream(spliterator(), false);
    }
}
</code></pre>

<h2>转换Stream</h2>
<p>转换Stream其实就是把一个Stream通过某些行为转换成一个新的Stream。Stream接口中定义了几个常用的转换方法，下面我们挑选几个常用的转换方法来解释。</p>
<blockquote>
<ul>
<li>distinct: 对于Stream中包含的元素进行去重操作（去重逻辑依赖元素的equals方法），新生成的Stream中没有重复的元素；</li>
</ul>
<blockquote>
<p>distinct方法示意图</p>
<blockquote>
<p><img src="http://img04.taobaocdn.com/imgextra/i4/90219132/T2K0lnXPRXXXXXXXXX_!!90219132.jpg" /></p>
</blockquote>
</blockquote>
<ul>
<li>filter: 对于Stream中包含的元素使用给定的过滤函数进行过滤操作，新生成的Stream只包含符合条件的元素；</li>
</ul>
<blockquote>
<p>filter方法示意图：</p>
<blockquote>
<p><img src="http://img03.taobaocdn.com/imgextra/i3/90219132/T2OxXnXPlXXXXXXXXX_!!90219132.jpg" /></p>
</blockquote>
</blockquote>
<ul>
<li>map: 对于Stream中包含的元素使用给定的转换函数进行转换操作，新生成的Stream只包含转换生成的元素。这个方法有三个对于原始类型的变种方法，分别是：mapToInt，mapToLong和mapToDouble。这三个方法也比较好理解，比如mapToInt就是把原始Stream转换成一个新的Stream，这个新生成的Stream中的元素都是int类型。之所以会有这样三个变种方法，可以免除自动装箱/拆箱的额外消耗；</li>
</ul>
<blockquote>
<p>map方法示意图：</p>
<blockquote>
<p><img src="http://img03.taobaocdn.com/imgextra/i3/90219132/T2PQJnXOJXXXXXXXXX_!!90219132.jpg" /></p>
</blockquote>
</blockquote>
<ul>
<li>flatMap：和map类似，不同的是其每个元素转换得到的是Stream对象，会把子Stream中的元素压缩到父集合中；</li>
</ul>
<blockquote>
<p>flatMap方法示意图：</p>
<blockquote>
<p><img src="http://img01.taobaocdn.com/imgextra/i1/90219132/T2mBXnXQhXXXXXXXXX_!!90219132.jpg" /></p>
</blockquote>
</blockquote>
<ul>
<li>peek: 生成一个包含原Stream的所有元素的新Stream，同时会提供一个消费函数（Consumer实例），新Stream每个元素被消费的时候都会执行给定的消费函数；</li>
</ul>
<blockquote>
<p>peek方法示意图：</p>
<blockquote>
<p><img src="http://img03.taobaocdn.com/imgextra/i3/90219132/T2DrFmXHtaXXXXXXXX_!!90219132.jpg" /></p>
</blockquote>
</blockquote>
<ul>
<li>limit: 对一个Stream进行截断操作，获取其前N个元素，如果原Stream中包含的元素个数小于N，那就获取其所有的元素；</li>
</ul>
<blockquote>
<p>limit方法示意图：</p>
<blockquote>
<p><img src="http://img02.taobaocdn.com/imgextra/i2/90219132/T2QAXlXJBaXXXXXXXX_!!90219132.jpg" /></p>
</blockquote>
</blockquote>
<ul>
<li>skip: 返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream，如果原Stream中包含的元素个数小于N，那么返回空Stream；</li>
</ul>
<blockquote>
<p>skip方法示意图：</p>
<blockquote>
<p><img src="http://img04.taobaocdn.com/imgextra/i4/90219132/T24A8mXUJXXXXXXXXX_!!90219132.jpg" /></p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>List&lt;Integer&gt; nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
System.out.println(“sum is:”+nums.stream().filter(num -&gt; num != null).
        distinct().mapToInt(num -&gt; num * 2).
        peek(System.out::println).skip(2).limit(4).sum());
</code></pre>

<p>这段代码演示了上面介绍的所有转换方法（除了flatMap），简单解释一下这段代码的含义：给定一个Integer类型的List，获取其对应的Stream对象，然后进行过滤掉null，再去重，再每个元素乘以2，再每个元素被消费的时候打印自身，在跳过前两个元素，最后去前四个元素进行加和运算(解释一大堆，很像废话，因为基本看了方法名就知道要做什么了。这个就是声明式编程的一大好处！)。大家可以参考上面对于每个方法的解释，看看最终的输出是什么。</p>
<h3>性能问题</h3>
<blockquote>
<p>有些细心的同学可能会有这样的疑问：在对于一个Stream进行多次转换操作，每次都对Stream的每个元素进行转换，而且是执行多次，这样时间复杂度就是一个for循环里把所有操作都做掉的N（转换的次数）倍啊。其实不是这样的，转换操作都是lazy的，多个转换操作只会在汇聚操作（见下节）的时候融合起来，一次循环完成。我们可以这样简单的理解，Stream里有个操作函数的集合，每次转换操作就是把转换函数放入这个集合中，在汇聚操作的时候循环Stream对应的集合，然后对每个元素执行所有的函数。</p>
</blockquote>
<h2>汇聚（Reduce）Stream</h2>
<p>汇聚这个词，是我自己翻译的在官方文档中是reduce，也叫fold。
在介绍汇聚操作之前，我们先看一下Java doc中对于其定义：</p>
<blockquote>
<p>A reduction operation (also called a fold) takes a sequence of input elements and combines them into a single summary result by repeated application of a combining operation, such as finding the sum or maximum of a set of numbers, or accumulating elements into a list. The streams classes have multiple forms of general reduction operations, called reduce() and collect(), as well as multiple specialized reduction forms such as sum(), max(), or count().</p>
</blockquote>
<p>简单翻译一下：汇聚操作（也称为折叠）接受一个元素序列为输入，反复使用某个合并操作，把序列中的元素合并成一个汇总的结果。比如查找一个数字列表的总和或者最大值，或者把这些数字累积成一个List对象。Stream接口有一些通用的汇聚操作，比如reduce()和collect()；也有一些特定用途的汇聚操作，比如sum(),max()和count()。注意：sum方法不是所有的Stream对象都有的，只有IntStream、LongStream和DoubleStream是实例才有。</p>
<p>下面会分两部分来介绍汇聚操作：</p>
<ol>
<li>可变汇聚：把输入的元素们累积到一个可变的容器中，比如Collection或者StringBuilder；</li>
<li>其他汇聚：除去可变汇聚剩下的，一般都不是通过反复修改某个可变对象，而是通过把前一次的汇聚结果当成下一次的入参，反复如此。比如reduce，count，allMatch；</li>
</ol>
<h3>可变汇聚</h3>
<p>可变汇聚对应的只有一个方法：collect，正如其名字显示的，它可以把Stream中的要有元素收集到一个结果容器中（比如Collection）。先看一下最通用的collect方法的定义（还有其他override方法）：</p>
<pre><code>&lt;R&gt; R collect(Supplier&lt;R&gt; supplier,
              BiConsumer&lt;R, ? super T&gt; accumulator,
              BiConsumer&lt;R, R&gt; combiner);
</code></pre>

<p>先来看看这三个参数的含义：Supplier supplier是一个工厂函数，用来生成一个新的容器；BiConsumer accumulator也是一个函数，用来把Stream中的元素添加到结果容器中；BiConsumer combiner还是一个函数，用来把中间状态的多个结果容器合并成为一个（并发的时候会用到）。看晕了？来段代码！</p>
<pre><code>List&lt;Integer&gt; nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
List&lt;Integer&gt; numsWithoutNull = nums.stream().filter(num -&gt; num != null).
        collect(() -&gt; new ArrayList&lt;Integer&gt;(),
                (list, item) -&gt; list.add(item),
                (list1, list2) -&gt; list1.addAll(list2));
</code></pre>

<p>上面这段代码就是对一个元素是Integer类型的List，先过滤掉全部的null，然后把剩下的元素收集到一个新的List中。进一步看一下collect方法的三个参数，都是lambda形式的函数。</p>
<blockquote>
<p>第一个函数生成一个新的ArrayList实例；
第二个函数接受两个参数，第一个是前面生成的ArrayList对象，二个是stream中包含的元素，函数体就是把stream中的元素加入ArrayList对象中。第二个函数被反复调用直到原stream的元素被消费完毕；
第三个函数也是接受两个参数，这两个都是ArrayList类型的，函数体就是把第二个ArrayList全部加入到第一个中；
但是上面的collect方法调用也有点太复杂了，没关系！我们来看一下collect方法另外一个override的版本，其依赖<a href="http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collector.html">Collector</a>。</p>
</blockquote>
<pre><code>&lt;R, A&gt; R collect(Collector&lt;? super T, A, R&gt; collector);
</code></pre>

<blockquote>
<p>这样清爽多了！少年，还有好消息，Java8还给我们提供了Collector的工具类–<a href="http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html">Collectors</a>，其中已经定义了一些静态工厂方法，比如：Collectors.toCollection()收集到Collection中, Collectors.toList()收集到List中和Collectors.toSet()收集到Set中。这样的静态方法还有很多，这里就不一一介绍了，大家可以直接去看JavaDoc。下面看看使用Collectors对于代码的简化：</p>
</blockquote>
<pre><code>List&lt;Integer&gt; numsWithoutNull = nums.stream().filter(num -&gt; num != null).
            collect(Collectors.toList());
</code></pre>

<h3>其他汇聚</h3>
<p>– reduce方法：</p>
<blockquote>
<p>reduce方法非常的通用，后面介绍的count，sum等都可以使用其实现。reduce方法有三个override的方法，本文介绍两个最常用的，最后一个留给读者自己学习。先来看reduce方法的第一种形式，其方法定义如下：</p>
</blockquote>
<pre><code>Optional&lt;T&gt; reduce(BinaryOperator&lt;T&gt; accumulator);
</code></pre>

<p>接受一个BinaryOperator类型的参数，在使用的时候我们可以用lambda表达式来。</p>
<pre><code>List&lt;Integer&gt; ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println(&quot;ints sum is:&quot; + ints.stream().reduce((sum, item) -&amp;gt; sum + item).get());
</code></pre>

<p>可以看到reduce方法接受一个函数，这个函数有两个参数，第一个参数是上次函数执行的返回值（也称为中间结果），第二个参数是stream中的元素，这个函数把这两个值相加，得到的和会被赋值给下次执行这个函数的第一个参数。要注意的是：<strong>第一次执行的时候第一个参数的值是Stream的第一个元素，第二个参数是Stream的第二个元素</strong>。这个方法返回值类型是Optional，这是Java8防止出现NPE的一种可行方法，后面的文章会详细介绍，这里就简单的认为是一个容器，其中可能会包含0个或者1个对象。</p>
<blockquote>
<p>这个过程可视化的结果如图：</p>
<blockquote>
<p><img src="http://img03.taobaocdn.com/imgextra/i3/90219132/T28rVAXJlaXXXXXXXX_!!90219132.jpg" /></p>
</blockquote>
</blockquote>
<p>reduce方法还有一个很常用的变种：</p>
<pre><code>T reduce(T identity, BinaryOperator&lt;T&gt; accumulator);
</code></pre>

<p>这个定义上上面已经介绍过的基本一致，不同的是：它允许用户提供一个循环计算的初始值，如果Stream为空，就直接返回该值。而且这个方法不会返回Optional，因为其不会出现null值。下面直接给出例子，就不再做说明了。</p>
<pre><code>List&lt;Integer&gt; ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println(&quot;ints sum is:&quot; + ints.stream().reduce(0, (sum, item) -&gt; sum + item));
</code></pre>

<p>– count方法：</p>
<blockquote>
<p>获取Stream中元素的个数。比较简单，这里就直接给出例子，不做解释了。</p>
</blockquote>
<pre><code>List&lt;Integer&gt; ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println(&quot;ints sum is:&quot; + ints.stream().count());
</code></pre>

<ul>
<li>搜索相关</li>
<li>allMatch：是不是Stream中的所有元素都满足给定的匹配条件</li>
<li>anyMatch：Stream中是否存在任何一个元素满足匹配条件</li>
<li>findFirst: 返回Stream中的第一个元素，如果Stream为空，返回空Optional</li>
<li>noneMatch：是不是Stream中的所有元素都不满足给定的匹配条件</li>
<li>
<p>max和min：使用给定的比较器（Operator），返回Stream中的最大|最小值
下面给出allMatch和max的例子，剩下的方法读者当成练习。</p>
<pre><code>List&lt;Integer&amp;gt; ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println(ints.stream().allMatch(item -&gt; item &lt; 100));
ints.stream().max((o1, o2) -&amp;gt; o1.compareTo(o2)).ifPresent(System.out::println);
</code></pre>

</li>
</ul>
<h2>引用文档</h2>
<ol>
<li>《Java SE 8 for the Really Impatient》</li>
<li>Java 8 API doc</li>
</ol>

</body>
</html>
<!-- This document was created with MarkdownPad, the Markdown editor for Windows (http://markdownpad.com) -->
